/** @file    BigBrotherImpl.cpp
 *  @author  Alessandro Polo
 *  @version $Id: BigBrotherParser.cpp 2808 2010-07-28 20:44:00Z alex $
 *  @brief
 * File containing methods for the BigBrotherImpl class.
 * The header for this class can be found in BigBrotherImpl.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

 #include "BigBrotherParser.h"

 #include <core/Message.h>
 #include <core/Request.h>
 #include <core/Response.h>
 #include <core/Fact.h>
 #include <core/MethodsCommon.h>
 #include <core/UserManager.h>
 #include <core/SecurityManager.h>

 #include <framework/building/Door.h>
 #include <framework/building/Room.h>
 #include <framework/building/Location.h>
 #include <interfaces/Device.h>
 #include <interfaces/devices/SensorMotion.h>
 #include <interfaces/devices/SensorLight.h>
 #include <interfaces/services/BuildingManager.h>


//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS

BigBrotherParser::BigBrotherParser( BundleGeneric& bundle ) : BundleGenericWorker(bundle)
 {
	this->timeoutEval = 0;

	this->locations = NULL;
 }

BigBrotherParser::~BigBrotherParser() {
	if ( isThreadRunning() ) {
		quitThread(10000);
	 }
 }

//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////// RUN THREAD

void BigBrotherParser::runThread()
 {
	Log(LOG_VERBOSE, ":runThread(): STARTING" );

	long counterEval = 0;
	while( this->running )
	 {
		ThreadImpl::sleepForMSec(1000);
		setThreadAlive();
		if ( this->timeoutEval == 0 ) // disabled
			continue;

		++counterEval;
		if ( counterEval < this->timeoutEval )
			continue;
		counterEval = 0;
//		evalTimeOut(); // remove timed-out comms/interp

		// update them all

	 }

	Log(LOG_VERBOSE, ":runThread(): EXITING" );
 }

////////////////////////////////////////////////////////////////////////////////////////////////// RUN THREAD
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT BigBrotherParser::updateHomeLocations()
 {
	Log(LOG_VERBOSE, ":updateHomeLocations() : Sending Request.." );

	DataList* dl1 = new DataList();
	dl1->add( "type="_Room_TYPE );
	dl1->add( _BuildingObject_KEY_Location );
	dl1->add( _KEY_parent );
	dl1->add( _Room_KEY_AccessTo );
	Message* message1 = new Message( new Request(_BuildingManager_METHOD_getchildprop, dl1) );
	message1->setSource(this->getOwner());
	message1->getDestination().fromString("*:Building");
	message1->setDestinationBus( _Bus_Core );
	message1->setSecurityToken( SecurityManager::generateToken(this->getOwner()->getPermission().getOwner()) );
	BusCore.postMessage(message1);
	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void BigBrotherParser::evalMessage( const Message& message )
 {
	if ( message.isEmpty() ) return;

	// add/remove/ update info of locations
	if ( message.getContent()->isResponse() ) {
		const Response* response = message.getContent()->asResponse();
		if ( !response->hasData() ) return;

		if ( response->getMethod() == _BuildingManager_METHOD_getchildprop ) {
			if ( !response->getData()->isKindOf<Table>() ) return;
			const wosh::Table* childrenprops = response->getData()->as<Table>();
			if ( childrenprops->getTableName() == "Properties of type="_Room_TYPE  )
				evalRooms(childrenprops, message.getSource() );
			else if ( childrenprops->getTableName() == "Properties of type="_Door_TYPE  )
				evalDoors(childrenprops, message.getSource() );
		 }
		else if ( response->getMethod() == _METHOD_List ) {
			if ( !response->getData()->isKindOf<Table>() ) return;
			const wosh::Table* children = response->getData()->as<Table>();
			evalRoom(children, message.getSource() );
		}
	 }

 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

// update locations (each ROOM is a Location) [_BuildingManager_METHOD_getchildprop type=_Room_TYPE]
void BigBrotherParser::evalRooms( const Table* childrenprops, const URI& )
 {
	this->locations->mutex().lockForWrite();
	for(unsigned int row=0; row<childrenprops->getRows(); row++ ) {
		const ISerializable* objURI = childrenprops->get(row, 0);
		const ISerializable* objKey = childrenprops->get(row, 1);
		const ISerializable* objValue = childrenprops->get(row, 2);
		if ( objURI == NULL || !objURI->isKindOf<Variant>() ) continue;
		if ( objKey == NULL || !objKey->isKindOf<Variant>() ) continue;
		if ( objValue == NULL || !objValue->isKindOf<Variant>() ) continue;
		const wosh::Data* dataURI = dynamic_cast<const wosh::Data*>(objURI);
		const wosh::Data* dataKey = dynamic_cast<const wosh::Data*>(objKey);
		const wosh::Data* dataValue = dynamic_cast<const wosh::Data*>(objValue);
		Location* cLoc = NULL;
		if ( dataKey->getData() == _BuildingObject_KEY_Location ) {
			cLoc = this->locations->key_( dataValue->getData() );
			if ( cLoc == NULL ) {
				cLoc = new Location();
				cLoc->getURI().fromString(dataURI->getData());
				cLoc->setName( dataValue->getData() );
				cLoc->setLocationType(Location::TYPE_BUILDING);
				this->locations->set_( cLoc->getName(), cLoc );
				Log(LOG_VERBOSE, ":evalRooms(): Added Location %s", cLoc->getName().c_str() );
			 }
		 }
	 }
	for(unsigned int row=0; row<childrenprops->getRows(); row++ ) {
		const ISerializable* objURI = childrenprops->get(row, 0);
		const ISerializable* objKey = childrenprops->get(row, 1);
		const ISerializable* objValue = childrenprops->get(row, 2);
		if ( objURI == NULL || !objURI->isKindOf<Variant>() ) continue;
		if ( objKey == NULL || !objKey->isKindOf<Variant>() ) continue;
		if ( objValue == NULL || !objValue->isKindOf<Variant>() ) continue;
		const wosh::Data* dataURI = dynamic_cast<const wosh::Data*>(objURI);
		const wosh::Data* dataKey = dynamic_cast<const wosh::Data*>(objKey);
		const wosh::Data* dataValue = dynamic_cast<const wosh::Data*>(objValue);
		if ( dataKey->getData() == _BuildingObject_KEY_Location ) continue;

		Location* cLocation = getLocationByURI_( URI(dataURI->getData()) );
		if ( cLocation == NULL ) {
			continue;
		 }
		if ( dataKey->getData() == _KEY_parent ) {
			Location* cParentLocation = getLocationByURI_( URI(dataValue->getData()) );
			cLocation->setParent(cParentLocation, true);
		 }
		else if ( dataKey->getData() == _Room_KEY_AccessTo ) {
			Location* cAccessToLocation = getLocationByURI_( URI(dataValue->getData()) );
			if ( cAccessToLocation != NULL )
				cLocation->addAccessTo(cAccessToLocation, true);
		 }

	 }
	this->locations->mutex().unLock();

	DataList* dl = new DataList();
	dl->add( "type="_Door_TYPE );
	dl->add( _KEY_parent );
	dl->add( _Door_KEY_AccessTo );
	Message* message = new Message( new Request(_BuildingManager_METHOD_getchildprop, dl) );
	message->setSource(this->getOwner()->getURI());
	message->getDestination().fromString("*:Building");
	message->setDestinationBus( _Bus_Core );
	message->setSecurityToken( SecurityManager::generateToken(this->getOwner()->getPermission().getOwner()) );
	BusCore.postMessage(message);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void BigBrotherParser::evalDoors( const Table* childrenprops, const URI& )
 {
	this->locations->mutex().lockForWrite();
	Location* cParentLocation = NULL;
	for(unsigned int row=0; row<childrenprops->getRows(); row++ ) {
		const ISerializable* objURI = childrenprops->get(row, 0);
		const ISerializable* objKey = childrenprops->get(row, 1);
		const ISerializable* objValue = childrenprops->get(row, 2);
		if ( objURI == NULL || !objURI->isKindOf<Variant>() ) continue;
		if ( objKey == NULL || !objKey->isKindOf<Variant>() ) continue;
		if ( objValue == NULL || !objValue->isKindOf<Variant>() ) continue;
//		const wosh::Data* dataURI = dynamic_cast<const wosh::Data*>(objURI);
		const wosh::Data* dataKey = dynamic_cast<const wosh::Data*>(objKey);
		const wosh::Data* dataValue = dynamic_cast<const wosh::Data*>(objValue);

		if ( dataKey->getData() == _KEY_parent ) {
			cParentLocation = getLocationByURI_( URI(dataValue->getData()) );
			continue;
		 }
		if ( cParentLocation == NULL ) continue;

		if ( dataKey->getData() == _Door_KEY_AccessTo ) {
			Location* cAccessToLocation = getLocationByURI_( URI(dataValue->getData()) );
			if ( cAccessToLocation != NULL )
				cParentLocation->addAccessTo(cAccessToLocation, true);
		 }

		cParentLocation = NULL;
	 }
	this->locations->mutex().unLock();

	Message* message = new Message( new Request(_METHOD_List) );
	message->setSource(this->getOwner()->getURI());
	message->getDestination().fromString("*:Building/*[ObjectType="_Room_TYPE"]");
	message->setDestinationBus( _Bus_Building );
	message->setSecurityToken( SecurityManager::generateToken(this->getOwner()->getPermission().getOwner()) );
	BusCore.postMessage(message);
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

// update 
void BigBrotherParser::evalDoor( const Table* props, const URI& )
 {
	if ( props == NULL ) return;
	string parentRoom = "";
	URI accessToRoom;
	for(unsigned int row=0; row<props->getRows(); row++ ) {
		const ISerializable* objName = props->get(row, 0);
		const ISerializable* objValue = props->get(row, 1);
		if ( objName == NULL ) continue;
		if ( objName->isKindOf<Property>() ) {
			const wosh::Property* prop = dynamic_cast<const wosh::Property*>(objName);
			if ( prop->getKey() == _KEY_parent )
				parentRoom = prop->getValue().getData();
			else if ( prop->getKey() == _Door_KEY_AccessTo )
				accessToRoom.fromString(prop->getValue().getData());
			continue;
		 }
		if ( !objName->isKindOf<Variant>() ) continue;
		if ( objValue == NULL || !objValue->isKindOf<Variant>() ) continue;
		const wosh::Data* dataName = dynamic_cast<const wosh::Data*>(objName);
		const wosh::Data* dataValue = dynamic_cast<const wosh::Data*>(objValue);

		if ( dataName->getData() == _KEY_parent )
			parentRoom = dataValue->getData();
		else if ( dataName->getData() == _Door_KEY_AccessTo )
			accessToRoom = dataValue->getData();
	 }
	if ( parentRoom.empty() || !accessToRoom.isValid() || accessToRoom.getName().empty() ) {
		return;
	 }
	this->locations->mutex().lockForWrite();
	Location* cLocationParent = this->locations->key_( "room_" + parentRoom );
	Location* cLocationAccess = this->locations->key_( "room_" + accessToRoom.getName() );
	if ( cLocationParent == NULL || cLocationAccess == NULL ) {
		this->locations->mutex().unLock();
		return;
	 }
	cLocationParent->addAccessTo(cLocationAccess, true);
	Log(LOG_VERBOSE, ":evalDoor(): Adding Access %s => %s", cLocationParent->getName().c_str(), cLocationAccess->getName().c_str() );

	this->locations->mutex().unLock();
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void BigBrotherParser::evalRoom( const Table* children, const URI& uri )
 {
	// each ROOM is an Location!
	this->locations->mutex().lockForWrite();
	Location* cLocation = getLocationByURI_( uri );
	if ( cLocation == NULL ) {
		///@todo force update
		this->locations->mutex().unLock();
		return;
	 }
	Log(LOG_VERBOSE, ":evalRoom(): Updating Location %s", cLocation->getName().c_str() );

	for(unsigned int row=0; row<children->getRows(); row++ ) {
		const ISerializable* objName = children->get(row, 0);
		const ISerializable* objInterfaces = children->get(row, 1);
		if ( objName == NULL || !objName->isKindOf<Variant>() ) continue;
		if ( objInterfaces == NULL || !objInterfaces->isKindOf<Variant>() ) continue;
		const wosh::Data* dataURI = dynamic_cast<const wosh::Data*>(objName);
		const wosh::Data* dataInterfaces = dynamic_cast<const wosh::Data*>(objInterfaces);

		cLocation->getDevices()[dataURI->getData()] = dataInterfaces->getData();
	 }

	this->locations->mutex().unLock();
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////


void BigBrotherParser::evalDevice_( const string& dev_uri, const string& dev_interfaces, const URI& )
 {
	URI sensorUri(dev_uri);
	Location* cLocation = NULL;
	if ( dev_interfaces.find( _SensorMotion_INTERFACE ) != string::npos ) {
		cLocation = this->locations->key_("sensor_" + sensorUri.getName());
		if ( cLocation == NULL ) {
			cLocation = new Location();
			cLocation->setURI(sensorUri);
			cLocation->setName( "sensor_" + sensorUri.getName() );
			cLocation->setLocationType(Location::TYPE_DEVICE);
			this->locations->set_( cLocation->getName(), cLocation );
			Log(LOG_VERBOSE, ":evalDevice_(): Added Location %s", cLocation->getName().c_str() );
		 }
		else {
			Log(LOG_VERBOSE, ":evalDevice_(): Updated Location %s", cLocation->getName().c_str() );
		 }
		std::map<string, Location*>::const_iterator it_c;
		std::map<string, Location*>::const_iterator it_c_end = this->locations->stdEnd();
		for ( it_c=this->locations->stdBegin(); it_c!=it_c_end; it_c++ ) {
			if ( it_c->second == NULL ) continue;
//			if ( it_c->second->isChildByUri(dev_uri) )
//				it_c->second->setChildLocation(cLocation, true);
		 }
	}
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void BigBrotherParser::evalDevices( const Table* listing, const URI& uri )
 {
	// each MOTION SENSOR is mapped to Location!

	vector<string> sensorsMotion;
	vector<string> sensorsLight;
	for(unsigned int row=0; row<listing->getRows(); row++ ) {
		const ISerializable* objName = listing->get(row, 0);
		const ISerializable* objInterfaces = listing->get(row, 4);
		if ( objName == NULL || !objName->isKindOf<Variant>() ) continue;
		if ( objInterfaces == NULL || !objInterfaces->isKindOf<Variant>() ) continue;
		const wosh::Data* dataURI = dynamic_cast<const wosh::Data*>(objName);
		const wosh::Data* dataInterfaces = dynamic_cast<const wosh::Data*>(objInterfaces);

		this->locations->mutex().lockForWrite();
		evalDevice_( dataURI->getData(), dataInterfaces->getData(), uri );
		this->locations->mutex().unLock();
	 }

 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Location* BigBrotherParser::getLocationByURI_( const URI& uri )
 {
	std::map<string, Location*>::const_iterator it_c;
	std::map<string, Location*>::const_iterator it_c_end = this->locations->stdEnd();
	for ( it_c=this->locations->stdBegin(); it_c!=it_c_end; it_c++ ) {
		if ( it_c->second == NULL ) continue;
		if ( it_c->second->getURI() != uri ) continue;
		return it_c->second;
	 }
	return NULL;
 }

Location* BigBrotherParser::getLocationParent_( const Location* child )
 {
	std::map<string, Location*>::const_iterator it_c;
	std::map<string, Location*>::const_iterator it_c_end = this->locations->stdEnd();
	for ( it_c=this->locations->stdBegin(); it_c!=it_c_end; it_c++ ) {
		if ( it_c->second == NULL ) continue;
		if ( it_c->second->getName() != child->getParentName() ) continue;
		return it_c->second;
	 }
	return NULL;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

